

#include "SkeletonMesh.h"
#include "anim.h"
#include "Bone.h"
#include "SkelMeshNode.h"
#include "model.h"
#include "ModelRCMgrIns.h"


//---------------------------------------------------------------------------------------------------------

CSkeletonMesh::CSkeletonMesh() : m_pFram(NULL),m_pBone(NULL),m_pmatCombine(NULL),
m_pParentModel(NULL),m_iType(_SMESH_GENERAL),m_pMatParentWorld(NULL),m_iIndex(0),
m_pMatParentShadow(NULL)
{
}

CSkeletonMesh::CSkeletonMesh(CModel* pModel,const int& iType,const int& iIndex ) : 
m_pFram(NULL),m_pBone(NULL),m_pmatCombine(NULL),m_pParentModel(pModel),m_iType(iType),
m_pMatParentWorld(NULL),m_iIndex(iIndex),m_pMatParentShadow(NULL)
{
}

 CSkeletonMesh::~CSkeletonMesh()
{
	Term();
}
void CSkeletonMesh::Term()
{
	if (m_pmatCombine)
	{
		delete [] m_pmatCombine;
		m_pmatCombine = NULL;
	}
	if (m_pFram)
	{
		delete m_pFram;
		m_pFram = NULL;
	}
	GDeleteAndRemoveElements(m_listMesh);
}

const uint32 CSkeletonMesh::GetBoneCount()
{
	return m_pBone->GetBoneCount();
}

void CSkeletonMesh::Bone2Frame(_FrameNode* pFram,_BoneNode* pBone,_BoneNode* pParentBone)
{
	pFram->SetBoneID(pBone->GetID());
	pFram->SetName(pBone->GetName());

	D3DXMATRIX mat = (pParentBone!=NULL) ? pBone->GetMat()*pParentBone->GetInvMat() : pBone->GetMat();
	pFram->SetMatrix(mat);
	_FrameNode* pChildFram = NULL;
	_BoneNode* pChildBone = NULL;
	GPOS pos = pBone->m_Children;
	while (pos)
	{
		pChildBone = pBone->m_Children.GetNext(pos);
		pChildFram = new _FrameNode(m_pFram);
		Bone2Frame(pChildFram,pChildBone,pBone);
		pFram->m_Children.Append(pChildFram);
	}
}
BOOL CSkeletonMesh::InitBone()
{
	BOOL bResult = TRUE;
	int iClass = ( m_iType == _SMESH_WEAPON || m_pParentModel->GetCharType() == _NPC) ? m_pParentModel->GetClass() : _CLASS_MAX;
	_ModelRCDes des(m_pParentModel->GetCharType(),m_iType,iClass,m_iIndex);
	m_pBone = CModelRCMgrIns::GetSingleton().GetBone(&des);
    if (m_pBone)
    {
		uint32 uBoneCount = m_pBone->GetBoneCount();
		m_pmatCombine =  new D3DXMATRIX[uBoneCount];

		SetParentWorldMat();

		m_pFram = new _Frame(this);
		Bone2Frame(&m_pFram->m_RootFrameNode,&m_pBone->m_RootBoneNode);
    }
	else
	{
		bResult = FALSE;
	}

	return bResult;	
}

BOOL CSkeletonMesh::ResetBone()
{
	Term();
	return InitBone();
}

void CSkeletonMesh::AddMesh(const int& iPartType,CSkelMeshNode* pMesh)
{
	float fRaduis = pMesh->m_vecMax.y * m_pBone->GetScale();
	m_pParentModel->SetRadius(fRaduis);
	CSkelMeshNodeInstance* pInstance = new CSkelMeshNodeInstance(iPartType);
	pInstance->SetMesh(pMesh);
	if (iPartType == _BODY)
	{
		m_listMesh.AddTail(pInstance);
	}
	else
	{
		m_listMesh.AddHead(pInstance);
	}
	
}

CSkelMeshNode* CSkeletonMesh::AddMesh(const _ModelRCDes* pDes)
{
	CSkelMeshNode* pMesh = CModelRCMgrIns::GetSingleton().GetSkelMesh(pDes);
	if (pMesh)
	{
		pMesh->m_pTexture = CModelRCMgrIns::GetSingleton().GetTexture(pDes);
		AddMesh(pDes->iPartType,pMesh);
	}
	return pMesh;	
}
CSkelMeshNode* CSkeletonMesh::AddMesh(const int& iPartType,const char* pszSkinName,const char* pszTexName)
{
	CSkelMeshNode* pMesh = CModelRCMgrIns::GetSingleton().GetSkelMesh(pszSkinName);
	if (pMesh)
	{
		pMesh->m_pTexture = CModelRCMgrIns::GetSingleton().GetTexture(pszTexName,m_pParentModel->GetCharType());
		AddMesh(iPartType,pMesh);
	}
	return pMesh;	
}
CSkelMeshNode* CSkeletonMesh::GetMeshNodeByType(const int& iPartType)
{
	CSkelMeshNodeInstance* pMeshNode = NULL;
	GPOS pos = m_listMesh;
	while (pos)
	{
		pMeshNode = m_listMesh.GetNext(pos);
		if (pMeshNode->GetType() == iPartType)
		{
			return pMeshNode->GetMesh();	
		}
	}

	return NULL;
}

BOOL CSkeletonMesh::ChangeMesh(const int& iPartType,const int& iIndex,const BOOL& bAdd)
{
	CSkelMeshNodeInstance* pMeshNode = NULL;
	CSkelMeshNodeInstance* pMeshNodeFind = NULL;

	GPOS posLast;
	GPOS pos = m_listMesh;
	while (pos)
	{
		posLast = pos;
		pMeshNode = m_listMesh.GetNext(pos);
		if (pMeshNode->GetType() == iPartType)
		{
			pMeshNodeFind = pMeshNode;

			if (!bAdd)
			{
				m_listMesh.RemoveAt(posLast);
				return TRUE;
			}
			break;			
		}
	}

	if (!bAdd && !pMeshNodeFind)
	{
		return FALSE;
	}

	BOOL bResult = TRUE;
	_ModelRCDes des;
	des.iCharType = m_pParentModel->GetCharType();
	des.iClass    = m_pParentModel->GetClass();
	des.iPartType = iPartType;
	des.iIndex    = iIndex;
	CSkelMeshNode* pNewNode = NULL;
	if (pMeshNodeFind)
	{
		pNewNode = CModelRCMgrIns::GetSingleton().GetSkelMesh(&des);
		if (!pNewNode)
		{
			bResult = FALSE;			
		}
		else
		{
			if(pNewNode != pMeshNodeFind->GetMesh())
			{			
				if (!pNewNode->m_pTexture)
				{
					pNewNode->m_pTexture = CModelRCMgrIns::GetSingleton().GetTexture(&des);					
				}	
				float fRaduis = pNewNode->m_vecMax.y * m_pBone->GetScale();
				m_pParentModel->SetRadius(fRaduis);
				pMeshNodeFind->SetMesh(pNewNode);
			}
		}		
	}
	else
	{
		pNewNode = AddMesh(&des);
		bResult = (pNewNode!=NULL);
	}

	return bResult;
}
BOOL CSkeletonMesh::ChangeMesh(const int& iPartType,const char* pszSkinName,const char* pszTexName,const BOOL& bAdd)
{
	CSkelMeshNodeInstance* pMeshNode = NULL;
	CSkelMeshNodeInstance* pMeshNodeFind = NULL;

	GPOS posLast;
	GPOS pos = m_listMesh;
	while (pos)
	{
		posLast = pos;
		pMeshNode = m_listMesh.GetNext(pos);
		if (pMeshNode->GetType() == iPartType)
		{
			pMeshNodeFind = pMeshNode;

			if (!bAdd)
			{
				m_listMesh.RemoveAt(posLast);
				return TRUE;
			}
			break;			
		}
	}

	if (!bAdd && !pMeshNodeFind)
	{
		return FALSE;
	}

	BOOL bResult = TRUE;
	CSkelMeshNode* pNewNode = NULL;
	if (pMeshNodeFind)
	{
		pNewNode = CModelRCMgrIns::GetSingleton().GetSkelMesh(pszSkinName);
		if (!pNewNode)
		{
			bResult = FALSE;
		}
		else
		{
			if( pNewNode != pMeshNode->GetMesh() )
			{
				if (!pNewNode->m_pTexture)
				{
					pNewNode->m_pTexture = CModelRCMgrIns::GetSingleton().GetTexture(pszTexName,m_pParentModel->GetCharType());
				}	
				float fRaduis = pNewNode->m_vecMax.y * m_pBone->GetScale();
				m_pParentModel->SetRadius(fRaduis);
				pMeshNodeFind->SetMesh(pNewNode);			
			}
		}		
	}
	else
	{
		pNewNode = AddMesh(iPartType, pszSkinName,pszTexName);
		bResult = (pNewNode!=NULL);
	}

	return bResult;
}
BOOL CSkeletonMesh::SetAnim(const _ModelRCDes* pDes,const _AniPlayOption& playOpt,const BOOL& bReset)
{
	BOOL bResult = FALSE;
	_Anim* pAnim = CModelRCMgrIns::GetSingleton().GetAnim(pDes);
	if (pAnim && m_pFram)
	{
		pAnim->SetAnimIndex(pDes->iIndex);
		m_pFram->SetAnim(pAnim,bReset);		

		m_pFram->m_iAniPlayOption = playOpt;
		return TRUE;
	}
	
	return bResult;
}

void CSkeletonMesh::GetBoneWorldMat(D3DXMATRIX& mat,const uint32 uBoneID)
{
	float fScale = m_pBone->m_Attr.scale;

	D3DXMatrixMultiply(&mat, &m_pBone->m_pBoneInverseMat[uBoneID], &m_pmatCombine[uBoneID]);

	// D3DXMatrixMultiplyTranspose(&mat, &mat, &m_mView);
	if (fScale!=1.0f)
	{
		D3DXMATRIX matScale;
		D3DXMatrixScaling(&matScale,fScale,fScale,fScale);
		mat *= matScale;
	}
	mat._41 += m_pParentModel->GetTransMat()._41;
	mat._42 += m_pParentModel->GetTransMat()._42;
	mat._43 += m_pParentModel->GetTransMat()._43;

	D3DXMatrixTranspose(&mat, &mat);
}

void CSkeletonMesh::SetParentWorldMat()
{
	if (m_iType == _SMESH_GENERAL )
	{
		m_pMatParentWorld = m_pParentModel->GetMatRot();
		m_pMatParentShadow = m_pParentModel->GetMatShadow();
	}
	else
	{
		CSkeletonMesh* pMesh = m_pParentModel->GetSkeMeshByType(_SMESH_GENERAL);
		if (!pMesh)
		{
			return;
		}

		const char* szBoneName = (m_iType == _SMESH_FACE) ? "Bip01 Head" : "Arm_Bone";
		m_pMatParentShadow = m_pMatParentWorld = pMesh->GetMatCombine(szBoneName);
	}
}

void CSkeletonMesh::CheckFrame(const float& fTime)
{
	m_pFram->CheckFrame(fTime);
}

void CSkeletonMesh::Update()
{
	m_pFram->Update(m_pMatParentWorld);
}

void CSkeletonMesh::UpdateShadow()
{	
	m_pFram->UpdateShadow(m_pMatParentShadow);
}

float* CSkeletonMesh::GetBottom()//render used
{
	D3DXMATRIX matt;
	D3DXMatrixMultiply(&matt,m_pBone->m_pBoneInverseMat, m_pmatCombine);

	return m_pParentModel->GetBottom(matt._42);
}

D3DXMATRIX* CSkeletonMesh::GetMatCombine(const char* szBoneNodeName)
{
	D3DXMATRIX* pMat = NULL;
	const _BoneNode* pNode = m_pBone->GetBoneNode(szBoneNodeName);
	if (pNode)
	{
		pMat = &m_pmatCombine[pNode->GetID()];
	}
	return pMat;
}
void CSkeletonMesh::SetDistance(float fDis)
{
	if (m_pBone->GetScale() != 1.0f)
	{
		fDis *= m_pBone->GetScale();
	}

	m_pParentModel->SetDistance(fDis);
}

float CSkeletonMesh::GetDistance() const
{
	return m_pParentModel->GetDistance();
};

BOOL CSkeletonMesh::SetEventFlag(const _AniEventType& eType,const _AniEventFLag& eFlag)
{
	if (m_pFram)
	{
		if (m_pFram->m_pAnim)
		{
		     return	m_pFram->m_pAnim->SetEventFlag(eType,eFlag);
		}
	}

	return FALSE;
}